﻿// Decompiled with JetBrains decompiler
// Type: Microsoft.InfoCards.InfoCardUIAgent
// Assembly: infocard, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// MVID: 8E14765A-6610-409A-BA36-099A0642905D
// Assembly location: E:\git\ALLIDA\windll\infocard.exe

using Microsoft.InfoCards.Diagnostics;
using Microsoft.Win32;
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Security.Principal;
using System.Threading;

namespace Microsoft.InfoCards
{
  internal class InfoCardUIAgent
  {
    public static readonly string UiAgentFullPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "icardagt.exe");
    private static ManualResetEvent s_doneShuttingDownEvent = new ManualResetEvent(false);
    private static InfoCardUIAgent.UInt32IndexedHybridDictionary s_sessionIdCollection = new InfoCardUIAgent.UInt32IndexedHybridDictionary();
    private static InfoCardUIAgent.UInt32IndexedHybridDictionary s_pidCollection = new InfoCardUIAgent.UInt32IndexedHybridDictionary();
    private static TimeSpan s_idleTimeout = new TimeSpan(0, 0, 300);
    private static object s_syncRoot = new object();
    private readonly TimeSpan AgentStateChangeTimeOut = new TimeSpan(0, 0, 300);
    private const string UiAgentName = "icardagt.exe";
    private const string IdleTimeoutKey = "software\\microsoft\\infocard";
    private const int IdleTimeoutDefault = 300;
    private const string IdleTimeoutValue = "sleep1";
    private static bool s_initialized;
    private static bool s_isShuttingDown;
    private ManualResetEvent m_uiStatusRecieved;
    private AutoResetEvent m_agentGetWorkStart;
    private AutoResetEvent m_agentGetWorkComplete;
    private ClientUIRequest m_request;
    private int m_tsSessionId;
    private Process m_process;
    private EventHandler m_processExitHandler;
    private Timer m_timer;
    private string m_trustedUserSid;
    private SafeNativeHandle m_trustedUserToken;
    private string m_desktopName;
    private string m_user;
    private InfoCardUIAgent.CallMode m_mode;
    private RpcUIAgentGetWorkCallback m_getWorkCallback;
    private ClientUIRequest.RequestResult m_currentStatus;
    private object m_memberSync;

    private InfoCardUIAgent(int callerPid, WindowsIdentity userIdentity, int tsSessionId)
    {
      this.m_agentGetWorkStart = new AutoResetEvent(false);
      this.m_agentGetWorkComplete = new AutoResetEvent(false);
      this.m_uiStatusRecieved = new ManualResetEvent(false);
      this.m_tsSessionId = tsSessionId;
      this.m_timer = (Timer) null;
      IntPtr zero1 = IntPtr.Zero;
      IntPtr zero2 = IntPtr.Zero;
      int pid = 0;
      this.m_mode = InfoCardUIAgent.CallMode.Sleep;
      this.m_currentStatus = ClientUIRequest.RequestResult.Pending;
      this.m_user = userIdentity.User.ToString();
      this.m_memberSync = new object();
      int num = NativeMcppMethods.VerifyTrust(InfoCardUIAgent.UiAgentFullPath);
      if (num != 0)
        InfoCardTrace.FailFast(SR.GetString("FailedToVerifySignature", (object) num));
      IntPtr zero3 = IntPtr.Zero;
      bool fElevateToken = false;
      int trustedUserWrapper = (int) NativeMcppMethods.CreateProcessAsTrustedUserWrapper(InfoCardUIAgent.UiAgentFullPath, "", (uint) callerPid, "WinSta0\\Default", userIdentity.Name, (uint) this.m_tsSessionId, ref zero2, ref zero1, ref pid, zero3, ref this.m_trustedUserSid, fElevateToken);
      if (trustedUserWrapper != 0)
        throw InfoCardTrace.ThrowHelperError((Exception) new FailedToStartupUIException(SR.GetString("CreateProcessFailed", (object) trustedUserWrapper), (Exception) new Win32Exception(trustedUserWrapper)));
      this.m_trustedUserToken = new SafeNativeHandle(zero2, true);
      using (SafeWaitHandle processHandle = new SafeWaitHandle(zero1, true))
      {
        this.m_process = ProcessMonitor.GetProcessById(pid);
        this.m_processExitHandler = new EventHandler(this.OnProcessExit);
        this.m_process.Exited += this.m_processExitHandler;
        Utility.ThrowIfProcessExited(processHandle);
      }
    }

    public uint ProcessId
    {
      get
      {
        return (uint) this.m_process.Id;
      }
    }

    public string DesktopName
    {
      get
      {
        return this.m_desktopName;
      }
    }

    private ClientUIRequest Request
    {
      get
      {
        return this.m_request;
      }
    }

    public int TsSessionId
    {
      get
      {
        return this.m_tsSessionId;
      }
    }

    public bool IsActive
    {
      get
      {
        switch (this.m_mode)
        {
          case InfoCardUIAgent.CallMode.GetToken:
          case InfoCardUIAgent.CallMode.Manage:
          case InfoCardUIAgent.CallMode.Import:
          case InfoCardUIAgent.CallMode.Error:
            return true;
          default:
            return false;
        }
      }
    }

    public bool IsShuttingDown
    {
      get
      {
        return this.m_mode == InfoCardUIAgent.CallMode.Shutdown;
      }
    }

    public SafeNativeHandle TrustedUserToken
    {
      get
      {
        return this.m_trustedUserToken;
      }
    }

    public string TrustedUserSid
    {
      get
      {
        return this.m_trustedUserSid;
      }
    }

    public string User
    {
      get
      {
        return this.m_user;
      }
    }

    private static void InitializeIfNecessary()
    {
      if (InfoCardUIAgent.s_initialized)
        return;
      lock (InfoCardUIAgent.s_syncRoot)
      {
        if (InfoCardUIAgent.s_initialized)
          return;
        using (RegistryKey registryKey = Registry.LocalMachine.OpenSubKey("software\\microsoft\\infocard", false))
        {
          if (registryKey != null)
          {
            object obj = registryKey.GetValue("sleep1", (object) 300);
            if (obj is int)
            {
              if ((int) obj > 0)
                InfoCardUIAgent.s_idleTimeout = new TimeSpan(0, 0, (int) obj);
            }
          }
        }
        InfoCardUIAgent.s_initialized = true;
      }
    }

    public static InfoCardUIAgent Create(
      int callerPid,
      WindowsIdentity callerIdentity,
      int tsSessionId)
    {
      InfoCardUIAgent.InitializeIfNecessary();
      InfoCardUIAgent infoCardUiAgent = (InfoCardUIAgent) null;
      lock (InfoCardUIAgent.s_syncRoot)
      {
        InfoCardUIAgent.ThrowIfShuttingDown();
        infoCardUiAgent = (InfoCardUIAgent) InfoCardUIAgent.s_sessionIdCollection[(uint) tsSessionId];
        if (infoCardUiAgent != null)
        {
          if (infoCardUiAgent.IsShuttingDown)
          {
            infoCardUiAgent = (InfoCardUIAgent) null;
          }
          else
          {
            if (infoCardUiAgent.Request != null)
              throw InfoCardTrace.ThrowHelperError((Exception) new ServiceBusyException());
            if (infoCardUiAgent.User != callerIdentity.User.ToString())
            {
              infoCardUiAgent.Shutdown();
              infoCardUiAgent = (InfoCardUIAgent) null;
            }
          }
        }
        if (infoCardUiAgent == null)
        {
          infoCardUiAgent = new InfoCardUIAgent(callerPid, callerIdentity, tsSessionId);
          InfoCardUIAgent.s_pidCollection[infoCardUiAgent.ProcessId] = (object) infoCardUiAgent;
          InfoCardUIAgent.s_sessionIdCollection[(uint) tsSessionId] = (object) infoCardUiAgent;
        }
      }
      return infoCardUiAgent;
    }

    public RpcUIAgentGetWorkCallback Bind(
      string desktopName,
      out SecurityIdentifier userSid,
      out SafeWaitHandle hStartEvent,
      out SafeWaitHandle hCompleteEvent)
    {
      lock (this.m_memberSync)
      {
        userSid = new SecurityIdentifier(this.m_trustedUserSid);
        hStartEvent = Utility.GetRemoteHandleFromLocalHandle(this.m_agentGetWorkStart.SafeWaitHandle, this.m_process);
        hCompleteEvent = Utility.GetRemoteHandleFromLocalHandle(this.m_agentGetWorkComplete.SafeWaitHandle, this.m_process);
        this.m_desktopName = desktopName;
        return this.m_getWorkCallback = new RpcUIAgentGetWorkCallback(this.GetWork);
      }
    }

    public void ResetUIResult()
    {
      this.m_currentStatus = ClientUIRequest.RequestResult.Pending;
      this.m_uiStatusRecieved.Reset();
    }

    public ClientUIRequest.RequestResult ShowUI(InfoCardUIAgent.CallMode mode)
    {
      if (this.m_currentStatus == ClientUIRequest.RequestResult.Pending)
      {
        bool flag = false;
        lock (this.m_memberSync)
        {
          if (this.m_currentStatus == ClientUIRequest.RequestResult.Pending)
          {
            switch (mode)
            {
              case InfoCardUIAgent.CallMode.GetToken:
              case InfoCardUIAgent.CallMode.Manage:
              case InfoCardUIAgent.CallMode.Import:
              case InfoCardUIAgent.CallMode.Error:
                if (this.SetMode(mode))
                {
                  flag = true;
                  break;
                }
                break;
              default:
                InfoCardTrace.ThrowInvalidArgumentConditional(true, nameof (mode));
                break;
            }
          }
        }
        if (flag)
          this.WaitForModeChange();
        this.m_uiStatusRecieved.WaitOne();
      }
      return this.m_currentStatus;
    }

    public void SetUIStatus(ClientUIRequest.RequestResult status)
    {
      lock (this.m_memberSync)
      {
        this.m_currentStatus = status;
        if (this.m_request != null && (ClientUIRequest.RequestResult.Cancel == this.m_currentStatus || ClientUIRequest.RequestResult.Untrusted == this.m_currentStatus || ClientUIRequest.RequestResult.Error == this.m_currentStatus))
          this.m_request.UserCancel(ClientUIRequest.RequestResult.Untrusted == this.m_currentStatus);
        this.m_uiStatusRecieved.Set();
      }
    }

    public void SetRequest(ClientUIRequest request)
    {
      InfoCardTrace.Assert(null != request, "Request should not be null");
      bool flag = false;
      lock (InfoCardUIAgent.s_syncRoot)
      {
        if (this.m_timer != null)
        {
          this.m_timer.Dispose();
          this.m_timer = (Timer) null;
        }
        this.m_request = request;
        if (this.SetMode(InfoCardUIAgent.CallMode.Initialize))
          flag = true;
        this.m_currentStatus = ClientUIRequest.RequestResult.Pending;
        this.m_uiStatusRecieved.Reset();
      }
      if (!flag)
        return;
      this.WaitForModeChange();
    }

    private bool SetMode(InfoCardUIAgent.CallMode mode)
    {
      bool flag = false;
      lock (this.m_memberSync)
      {
        if (InfoCardUIAgent.CallMode.Shutdown != this.m_mode)
        {
          if (this.m_mode != mode)
          {
            switch (mode)
            {
              case InfoCardUIAgent.CallMode.Sleep:
                InfoCardTrace.ThrowInvalidArgumentConditional(this.m_mode != InfoCardUIAgent.CallMode.Release, nameof (mode));
                goto case InfoCardUIAgent.CallMode.Shutdown;
              case InfoCardUIAgent.CallMode.Initialize:
                InfoCardTrace.ThrowInvalidArgumentConditional(this.m_mode != InfoCardUIAgent.CallMode.Sleep, nameof (mode));
                goto case InfoCardUIAgent.CallMode.Shutdown;
              case InfoCardUIAgent.CallMode.GetToken:
              case InfoCardUIAgent.CallMode.Manage:
              case InfoCardUIAgent.CallMode.Import:
              case InfoCardUIAgent.CallMode.Error:
                InfoCardTrace.ThrowInvalidArgumentConditional(this.m_mode != InfoCardUIAgent.CallMode.Initialize, nameof (mode));
                goto case InfoCardUIAgent.CallMode.Shutdown;
              case InfoCardUIAgent.CallMode.Shutdown:
label_10:
                this.m_mode = mode;
                flag = true;
                break;
              case InfoCardUIAgent.CallMode.Release:
                switch (this.m_mode)
                {
                  case InfoCardUIAgent.CallMode.Initialize:
                  case InfoCardUIAgent.CallMode.GetToken:
                  case InfoCardUIAgent.CallMode.Manage:
                  case InfoCardUIAgent.CallMode.Import:
                  case InfoCardUIAgent.CallMode.Error:
                    goto label_10;
                  default:
                    InfoCardTrace.ThrowInvalidArgumentConditional(true, nameof (mode));
                    goto label_10;
                }
              default:
                InfoCardTrace.ThrowInvalidArgumentConditional(true, nameof (mode));
                goto case InfoCardUIAgent.CallMode.Shutdown;
            }
          }
        }
      }
      return flag;
    }

    public void ReleaseUI()
    {
      if (!this.IsActive && InfoCardUIAgent.CallMode.Initialize != this.m_mode || !this.SetMode(InfoCardUIAgent.CallMode.Release))
        return;
      this.WaitForModeChange();
    }

    private void WaitForModeChange()
    {
      this.m_agentGetWorkStart.Set();
      if (!this.m_agentGetWorkComplete.WaitOne(this.AgentStateChangeTimeOut, false))
      {
        Utility.KillHelper(this.m_process);
        throw InfoCardTrace.ThrowHelperError((Exception) new CommunicationException(SR.GetString("UIAgentCrash")));
      }
    }

    private void GetWork(out InfoCardUIAgent.CallMode mode, out int requestHandle)
    {
      lock (this.m_memberSync)
      {
        mode = InfoCardUIAgent.CallMode.Shutdown;
        requestHandle = 0;
        try
        {
          mode = this.m_mode;
          if (this.m_request != null)
            requestHandle = this.m_request.RequestHandle;
          if (mode != InfoCardUIAgent.CallMode.Sleep)
            return;
          this.m_timer = new Timer(InfoCardTrace.ThunkCallback(new TimerCallback(this.OnTimeout)), (object) null, InfoCardUIAgent.s_idleTimeout, new TimeSpan(0, 0, 0, 0, -1));
        }
        catch (Exception ex)
        {
          if (InfoCardTrace.IsFatal(ex))
            InfoCardService.Crash(ex);
          ClientUIRequest request = this.Request;
          if (request == null)
            return;
          request.ProcessingException = ex;
        }
      }
    }

    public void ClearRequest(ClientUIRequest request)
    {
      bool flag = false;
      lock (InfoCardUIAgent.s_syncRoot)
      {
        if (this.m_request == request)
        {
          if (this.SetMode(InfoCardUIAgent.CallMode.Sleep))
            flag = true;
          this.m_request = (ClientUIRequest) null;
        }
      }
      if (!flag)
        return;
      this.WaitForModeChange();
      if (ClientUIRequest.RequestResult.Error != this.m_currentStatus)
        return;
      this.Shutdown();
    }

    private void OnTimeout(object state)
    {
      lock (InfoCardUIAgent.s_syncRoot)
      {
        if (this.m_request != null || this.IsShuttingDown)
          return;
        this.Shutdown();
      }
    }

    private void OnProcessExit(int exitCode)
    {
      lock (InfoCardUIAgent.s_syncRoot)
      {
        this.m_mode = InfoCardUIAgent.CallMode.Shutdown;
        InfoCardUIAgent.s_pidCollection.Remove(this.ProcessId);
        InfoCardUIAgent.s_sessionIdCollection.Remove((uint) this.TsSessionId);
        if (this.m_request != null && (this.m_currentStatus == ClientUIRequest.RequestResult.Pending || ClientUIRequest.RequestResult.Ok == this.m_currentStatus))
        {
          switch ((EventCode) exitCode)
          {
            case EventCode.E_ICARD_UNTRUSTED:
              this.m_request.UserCancel(true);
              this.m_currentStatus = ClientUIRequest.RequestResult.Untrusted;
              break;
            case EventCode.E_ICARD_USERCANCELLED:
              this.m_request.UserCancel(false);
              this.m_currentStatus = ClientUIRequest.RequestResult.Cancel;
              break;
            case EventCode.E_ICARD_UI_INITIALIZATION:
              this.m_request.UserCancel(false);
              this.m_currentStatus = ClientUIRequest.RequestResult.UIFailedInitialization;
              break;
            default:
              this.m_request.UserCancel(false);
              this.m_currentStatus = ClientUIRequest.RequestResult.UICrashed;
              break;
          }
          this.m_uiStatusRecieved.Set();
        }
        this.m_agentGetWorkComplete.Set();
        if (InfoCardUIAgent.s_isShuttingDown && InfoCardUIAgent.s_pidCollection.Count == 0)
          InfoCardUIAgent.s_doneShuttingDownEvent.Set();
        if (this.m_timer != null)
        {
          this.m_timer.Dispose();
          this.m_timer = (Timer) null;
        }
        if (this.m_trustedUserToken == null)
          return;
        this.m_trustedUserToken.Dispose();
        this.m_trustedUserToken = (SafeNativeHandle) null;
      }
    }

    private void OnProcessExit(object sender, EventArgs e)
    {
      this.m_process.Exited -= this.m_processExitHandler;
      Process process = sender as Process;
      InfoCardTrace.Assert(null != process, "Should be of type process.");
      InfoCardTrace.Assert(process.ExitCode == this.m_process.ExitCode, "Should be the same!");
      this.OnProcessExit(process.ExitCode);
    }

    private static void ThrowIfShuttingDown()
    {
      if (InfoCardUIAgent.s_isShuttingDown)
        throw InfoCardTrace.ThrowHelperError((Exception) new SystemShuttingDownException());
    }

    private void Shutdown()
    {
      if (!this.SetMode(InfoCardUIAgent.CallMode.Shutdown))
        return;
      this.WaitForModeChange();
    }

    private void Kill()
    {
      Utility.KillHelper(this.m_process);
    }

    public static void OnLogout(uint sessionId)
    {
      if (!InfoCardUIAgent.s_initialized)
        return;
      lock (InfoCardUIAgent.s_syncRoot)
        ((InfoCardUIAgent) InfoCardUIAgent.s_sessionIdCollection[sessionId])?.Shutdown();
    }

    public static void DoShutdown()
    {
      bool flag = false;
      if (!InfoCardUIAgent.s_initialized)
        return;
      lock (InfoCardUIAgent.s_syncRoot)
      {
        InfoCardUIAgent.s_isShuttingDown = true;
        foreach (InfoCardUIAgent infoCardUiAgent in (IEnumerable) InfoCardUIAgent.s_sessionIdCollection.Values)
        {
          flag = true;
          infoCardUiAgent.Shutdown();
        }
      }
      if (!flag || InfoCardUIAgent.s_doneShuttingDownEvent.WaitOne(new TimeSpan(0, 0, 15), false))
        return;
      lock (InfoCardUIAgent.s_syncRoot)
      {
        foreach (InfoCardUIAgent infoCardUiAgent in (IEnumerable) InfoCardUIAgent.s_sessionIdCollection.Values)
          infoCardUiAgent.Kill();
      }
    }

    public static InfoCardUIAgent FindByPid(uint pid)
    {
      lock (InfoCardUIAgent.s_syncRoot)
        return (InfoCardUIAgent) InfoCardUIAgent.s_pidCollection[pid];
    }

    private class UInt32IndexedHybridDictionary
    {
      private HybridDictionary m_realDictionary = new HybridDictionary();

      public int Count
      {
        get
        {
          return this.m_realDictionary.Count;
        }
      }

      public ICollection Values
      {
        get
        {
          return this.m_realDictionary.Values;
        }
      }

      public object this[uint index]
      {
        get
        {
          return this.m_realDictionary[(object) index];
        }
        set
        {
          this.m_realDictionary[(object) index] = value;
        }
      }

      public void Remove(uint index)
      {
        this.m_realDictionary.Remove((object) index);
      }
    }

    public enum CallMode
    {
      Sleep,
      Initialize,
      GetToken,
      Manage,
      Import,
      Shutdown,
      Crash,
      Error,
      Release,
    }
  }
}
